/*
Copyright 2025 PerfXLab (Beijing) Technologies Co., Ltd.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
 * @file OpenSpB_vector.c
 * @xulei (xulei19b@ict.ac.com)
 * @brief Member functions of OpenSpB_vector
 * @version 0.1
 * @date 2022-08-24
 *
 * @copyright Copyright (c) 2022
 *
 */
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "OpenSpB_core.h"
#include "OpenSpB_Global.h"
#define max(x, y) (x > y) ? x : y;
#define Vector_compute_next_grow(size) \
    ((size) ? ((size) << 1) : 1)

struct OpenSpB_vector_opaque OpenSpB_vector_null_opaque =
    {OpenSpB_null_type,
     OpenSpB_vector_no_format,
     0,
     0};
OpenSpB_vector OpenSpB_vector_null = &OpenSpB_vector_null_opaque;

OpenSpB_info OpenSpB_vector_nvals(OpenSpB_index *n, OpenSpB_vector v)
{
    *n = v->n;
    return OpenSpB_success;
}

OpenSpB_info OpenSpB_vector_new(
    OpenSpB_vector *v,
    OpenSpB_type type,
    OpenSpB_index n)
{

    OpenSpB_vector_format_value_ext format_type =
        OpenSpB_Global_get_Vector_Format();
    OpenSpB_info info;

    switch (format_type)
    {
    case (OpenSpB_dense):
        info = OpenSpB_vector_Dense_new(v, type, n);
        return info;
    case (OpenSpB_sparse):
        info = OpenSpB_vector_Sparse_new(v, type, n);
        return info;
    default:
        OpenSpB_Error("Unknown Vector Format \
in the OpenSpB_vector_new!\n",
                      __FILE__, __LINE__);
        break;
    }
    return OpenSpB_failure;
}

// Todo : This is a very slow implementation.
OpenSpB_info OpenSpB_vector_dup(OpenSpB_vector *v1, OpenSpB_vector v2)
{
    OpenSpB_vector_format_value_ext format_type = v2->format_type;
    OpenSpB_info info;

    switch (format_type)
    {
    case (OpenSpB_dense):
        info = OpenSpB_vector_Dense_dup(v1, v2);
        return info;
    case (OpenSpB_sparse):
        OpenSpB_Error("GxB_SPARSE is not supported \
for Vector Format in the OpenSpB_vector_dup!\n",
                      __FILE__, __LINE__);
        return OpenSpB_failure;
    default:
        OpenSpB_Error("Unknown vector format_type \
in the OpenSpB_vector_dup!\n",
                      __FILE__, __LINE__);
        return OpenSpB_failure;
    }
}

OpenSpB_info OpenSpB_vector_copy_format_ext(
    OpenSpB_vector *out,
    const OpenSpB_vector in)
{
    OpenSpB_info info;
    if (*out != NULL)
    {
        SAFE_CALL(OpenSpB_vector_free(out));
    }

    const OpenSpB_type type = in->element_type;
    const OpenSpB_index n = in->n;
    const OpenSpB_vector_format_value_ext format =
        in->format_type;

    info = OpenSpB_vector_new_format_ext(
        out, type, n, format);

    return info;
}

OpenSpB_info OpenSpB_vector_new_format_ext(
    OpenSpB_vector *v,
    OpenSpB_type type,
    OpenSpB_index n,
    OpenSpB_vector_format_value_ext format)
{
    OpenSpB_info info;

    switch (format)
    {
    case (OpenSpB_dense):
        info = OpenSpB_vector_Dense_new(v, type, n);
        return info;
        break;
    case (OpenSpB_sparse):
        info = OpenSpB_vector_Sparse_new(v, type, n);
        return info;
        break;
    default:
        OpenSpB_Error("Unknown Vector Format \
in the OpenSpB_vector_new!\n",
                      __FILE__, __LINE__);
        break;
    }
    return OpenSpB_failure;
}

OpenSpB_vector_format_value_ext OpenSpB_vector_format_ext(const OpenSpB_vector v)
{
    return v->format_type;
}

#ifdef _OPENMP
#define GRB_VECTOR_SET(type, T)                              \
    OpenSpB_info OpenSpB_vector_set_element_##T(              \
        OpenSpB_vector v,                                    \
        type value,                                          \
        OpenSpB_index index)                                 \
    {                                                        \
        OpenSpB_vector_format_value_ext format =             \
            v->format_type;                                  \
        OpenSpB_info info;                                   \
        switch (format)                                      \
        {                                                    \
        case (OpenSpB_dense):                                \
            return OpenSpB_vector_Dense_setElement_##T(      \
                v, value, index);                            \
        case (OpenSpB_sparse):                               \
        {                                                    \
            _Pragma("omp critical")                          \
            {                                                \
                info = OpenSpB_vector_Sparse_setElement_##T( \
                    v, value, index);                        \
            }                                                \
            return info;                                     \
        }                                                    \
        default:                                             \
            OpenSpB_Error("Unknown Vector Format \
in the OpenSpB_vector_set_element_##T !\n",                   \
                          __FILE__, __LINE__);               \
            break;                                           \
        }                                                    \
        return OpenSpB_failure;                              \
    }
#else
#define GRB_VECTOR_SET(type, T)                          \
    OpenSpB_info OpenSpB_vector_set_element_##T(          \
        OpenSpB_vector v,                                \
        type value,                                      \
        OpenSpB_index index)                             \
    {                                                    \
        OpenSpB_vector_format_value_ext format =         \
            v->format_type;                              \
        OpenSpB_info info;                               \
        switch (format)                                  \
        {                                                \
        case (OpenSpB_dense):                            \
        {                                                \
            info = OpenSpB_vector_Dense_setElement_##T(  \
                v, value, index);                        \
            return info;                                 \
        }                                                \
        case (OpenSpB_sparse):                           \
        {                                                \
            info = OpenSpB_vector_Sparse_setElement_##T( \
                v, value, index);                        \
            return info;                                 \
        }                                                \
        default:                                         \
            OpenSpB_error("Unknown Vector Format \
in the OpenSpB_vector_set_element_##T !\n");              \
            break;                                       \
        }                                                \
        return OpenSpB_failure;                          \
    }
#endif
GRB_VECTOR_SET(bool, BOOL);
GRB_VECTOR_SET(int8_t, INT8);
GRB_VECTOR_SET(int16_t, INT16);
GRB_VECTOR_SET(int32_t, INT32);
GRB_VECTOR_SET(int64_t, INT64);
GRB_VECTOR_SET(uint8_t, UINT8);
GRB_VECTOR_SET(uint16_t, UINT16);
GRB_VECTOR_SET(uint32_t, UINT32);
GRB_VECTOR_SET(uint64_t, UINT64);
GRB_VECTOR_SET(float, FP32);
GRB_VECTOR_SET(double, FP64);
#undef GRB_VECTOR_SET

#define CASE_SWICTH_DATA_TYPE_A_TO_B(                  \
    type_A, T_A, type_B, T_B)                          \
    case OpenSpB_##T_B:                                \
        info = OpenSpB_vector_set_element_##T_B(        \
            v, (type_B)(s->value.T_A##_value), index); \
        break;

#define CASES_SWICTH_DATA_TYPE_A_TO_B(type_A, T_A) \
    switch (v->element_type)                       \
    {                                              \
        CASE_SWICTH_DATA_TYPE_A_TO_B(              \
            type_A, T_A, bool, BOOL);              \
        CASE_SWICTH_DATA_TYPE_A_TO_B(              \
            type_A, T_A, int8_t, INT8);            \
        CASE_SWICTH_DATA_TYPE_A_TO_B(              \
            type_A, T_A, int16_t, INT16);          \
        CASE_SWICTH_DATA_TYPE_A_TO_B(              \
            type_A, T_A, int32_t, INT32);          \
        CASE_SWICTH_DATA_TYPE_A_TO_B(              \
            type_A, T_A, int64_t, INT64);          \
        CASE_SWICTH_DATA_TYPE_A_TO_B(              \
            type_A, T_A, uint8_t, UINT8);          \
        CASE_SWICTH_DATA_TYPE_A_TO_B(              \
            type_A, T_A, uint16_t, UINT16);        \
        CASE_SWICTH_DATA_TYPE_A_TO_B(              \
            type_A, T_A, uint32_t, UINT32);        \
        CASE_SWICTH_DATA_TYPE_A_TO_B(              \
            type_A, T_A, uint64_t, UINT64);        \
        CASE_SWICTH_DATA_TYPE_A_TO_B(              \
            type_A, T_A, float, FP32);             \
        CASE_SWICTH_DATA_TYPE_A_TO_B(              \
            type_A, T_A, double, FP64);            \
    default:                                       \
        OpenSpB_Error("unknown element type \
of the scalar !\n",                                \
                      __FILE__, __LINE__);         \
        break;                                     \
    }

#define CASE_SETELEMENT_SCALAR(type_A, T_A) \
    case OpenSpB_##T_A:                     \
        CASES_SWICTH_DATA_TYPE_A_TO_B(      \
            type_A, T_A);                   \
        break;

#define CASES_SETELEMENT_SCALAR               \
    CASE_SETELEMENT_SCALAR(bool, BOOL);       \
    CASE_SETELEMENT_SCALAR(int8_t, INT8);     \
    CASE_SETELEMENT_SCALAR(int16_t, INT16);   \
    CASE_SETELEMENT_SCALAR(int32_t, INT32);   \
    CASE_SETELEMENT_SCALAR(int64_t, INT64);   \
    CASE_SETELEMENT_SCALAR(uint8_t, UINT8);   \
    CASE_SETELEMENT_SCALAR(uint16_t, UINT16); \
    CASE_SETELEMENT_SCALAR(uint32_t, UINT32); \
    CASE_SETELEMENT_SCALAR(uint64_t, UINT64); \
    CASE_SETELEMENT_SCALAR(float, FP32);      \
    CASE_SETELEMENT_SCALAR(double, FP64);

OpenSpB_info OpenSpB_vector_set_element_Scalar(
    OpenSpB_vector v,
    OpenSpB_scalar s,
    OpenSpB_index index)
{
    OpenSpB_info info = OpenSpB_failure;
    switch (s->element_type)
    {
        CASES_SETELEMENT_SCALAR;
    default:
        OpenSpB_Error("unknown element type of scalar \
in the OpenSpB_vector_set_element_Scalar!\n",
                      __FILE__, __LINE__);
        break;
    }
    return info;
}
#undef CASES_SETELEMENT_SCALAR
#undef CASE_SETELEMENT_SCALAR
#undef CASES_SWICTH_DATA_TYPE_A_TO_B
#undef CASE_SWICTH_DATA_TYPE_A_TO_B

#define GRB_VECTOR_EXTRACT(type, T)                          \
    OpenSpB_info OpenSpB_vector_extract_element_##T(          \
        type *value,                                         \
        OpenSpB_vector v,                                    \
        OpenSpB_index index)                                 \
    {                                                        \
        OpenSpB_vector_format_value_ext format =             \
            v->format_type;                                  \
        switch (format)                                      \
        {                                                    \
        case (OpenSpB_dense):                                \
            return OpenSpB_vector_Dense_extractElement_##T(  \
                value, v, index);                            \
        case (OpenSpB_sparse):                               \
            return OpenSpB_vector_Sparse_extractElement_##T( \
                value, v, index);                            \
            break;                                           \
        default:                                             \
            OpenSpB_Error("Unknown Vector Format \
in the OpenSpB_vector_extract_element_" #T " !\n",            \
                          __FILE__, __LINE__);               \
            break;                                           \
        }                                                    \
        return OpenSpB_failure;                              \
    }

GRB_VECTOR_EXTRACT(bool, BOOL);
GRB_VECTOR_EXTRACT(int8_t, INT8);
GRB_VECTOR_EXTRACT(int16_t, INT16);
GRB_VECTOR_EXTRACT(int32_t, INT32);
GRB_VECTOR_EXTRACT(int64_t, INT64);
GRB_VECTOR_EXTRACT(uint8_t, UINT8);
GRB_VECTOR_EXTRACT(uint16_t, UINT16);
GRB_VECTOR_EXTRACT(uint32_t, UINT32);
GRB_VECTOR_EXTRACT(uint64_t, UINT64);
GRB_VECTOR_EXTRACT(float, FP32);
GRB_VECTOR_EXTRACT(double, FP64);
#undef GRB_VECTOR_EXTRACT

#define CASE_SWICTH_DATA_TYPE_A_TO_B(   \
    type_A, T_A, type_B, T_B)           \
    case OpenSpB_##T_B:                 \
        s->value.T_B##_value =          \
            (type_B)(temp.T_A##_value); \
        break;

#define CASES_SWICTH_DATA_TYPE_A_TO_B(type_A, T_A) \
    switch (s->element_type)                       \
    {                                              \
        CASE_SWICTH_DATA_TYPE_A_TO_B(              \
            type_A, T_A, bool, BOOL);              \
        CASE_SWICTH_DATA_TYPE_A_TO_B(              \
            type_A, T_A, int8_t, INT8);            \
        CASE_SWICTH_DATA_TYPE_A_TO_B(              \
            type_A, T_A, int16_t, INT16);          \
        CASE_SWICTH_DATA_TYPE_A_TO_B(              \
            type_A, T_A, int32_t, INT32);          \
        CASE_SWICTH_DATA_TYPE_A_TO_B(              \
            type_A, T_A, int64_t, INT64);          \
        CASE_SWICTH_DATA_TYPE_A_TO_B(              \
            type_A, T_A, uint8_t, UINT8);          \
        CASE_SWICTH_DATA_TYPE_A_TO_B(              \
            type_A, T_A, uint16_t, UINT16);        \
        CASE_SWICTH_DATA_TYPE_A_TO_B(              \
            type_A, T_A, uint32_t, UINT32);        \
        CASE_SWICTH_DATA_TYPE_A_TO_B(              \
            type_A, T_A, uint64_t, UINT64);        \
        CASE_SWICTH_DATA_TYPE_A_TO_B(              \
            type_A, T_A, float, FP32);             \
        CASE_SWICTH_DATA_TYPE_A_TO_B(              \
            type_A, T_A, double, FP64);            \
    default:                                       \
        OpenSpB_Error("unknown element type \
of the scalar !\n",                                \
                      __FILE__, __LINE__);         \
        break;                                     \
    }

#define CASE_EXTRACTELEMENT_SCALAR(type_A, T_A)     \
    case OpenSpB_##T_A:                             \
        info = OpenSpB_vector_extract_element_##T_A( \
            &(temp.T_A##_value), v, index);         \
        if (info == OpenSpB_success)                \
            CASES_SWICTH_DATA_TYPE_A_TO_B(          \
                type_A, T_A);                       \
        break;

#define CASES_EXTRACTELEMENT_SCALAR               \
    CASE_EXTRACTELEMENT_SCALAR(bool, BOOL);       \
    CASE_EXTRACTELEMENT_SCALAR(int8_t, INT8);     \
    CASE_EXTRACTELEMENT_SCALAR(int16_t, INT16);   \
    CASE_EXTRACTELEMENT_SCALAR(int32_t, INT32);   \
    CASE_EXTRACTELEMENT_SCALAR(int64_t, INT64);   \
    CASE_EXTRACTELEMENT_SCALAR(uint8_t, UINT8);   \
    CASE_EXTRACTELEMENT_SCALAR(uint16_t, UINT16); \
    CASE_EXTRACTELEMENT_SCALAR(uint32_t, UINT32); \
    CASE_EXTRACTELEMENT_SCALAR(uint64_t, UINT64); \
    CASE_EXTRACTELEMENT_SCALAR(float, FP32);      \
    CASE_EXTRACTELEMENT_SCALAR(double, FP64);

OpenSpB_info OpenSpB_vector_extract_element_Scalar(
    OpenSpB_scalar s,
    const OpenSpB_vector v,
    const OpenSpB_index index)
{
    OpenSpB_info info;
    OpenSpB_type v_tpye = v->element_type;
    Scalar_Value temp;
    switch (v_tpye)
    {
        CASES_EXTRACTELEMENT_SCALAR;
    default:
        OpenSpB_Error("Unknown the scalar element type\
of the input vector \
in the OpenSpB_vector_extract_element_Scalar\n",
                      __FILE__, __LINE__);
        info = OpenSpB_failure;
        break;
    }
    return info;
}
#undef CASES_EXTRACTELEMENT_SCALAR
#undef CASE_EXTRACTELEMENT_SCALAR
#undef CASES_SWICTH_DATA_TYPE_A_TO_B
#undef CASE_SWICTH_DATA_TYPE_A_TO_B

#define OpenSpB_VECTOR_EXTRACT_TUPLES(type, T)              \
    OpenSpB_info OpenSpB_vector_extract_tuples_##T(          \
        OpenSpB_index *indices,                             \
        type *values,                                       \
        OpenSpB_index *n,                                   \
        const OpenSpB_vector v)                             \
    {                                                       \
        OpenSpB_vector_format_value_ext format =            \
            v->format_type;                                 \
        switch (format)                                     \
        {                                                   \
        case (OpenSpB_dense):                               \
            return OpenSpB_vector_Dense_extractTuples_##T(  \
                indices, values, n, v);                     \
        case (OpenSpB_sparse):                              \
            return OpenSpB_vector_Sparse_extractTuples_##T( \
                indices, values, n, v);                     \
        default:                                            \
            OpenSpB_Error("Unknown Vector Format \
in OpenSpB_vector_extract_tuples_" #T " !\n",                \
                          __FILE__, __LINE__);              \
            break;                                          \
        }                                                   \
        return OpenSpB_failure;                             \
    }
OpenSpB_VECTOR_EXTRACT_TUPLES(bool, BOOL);
OpenSpB_VECTOR_EXTRACT_TUPLES(int8_t, INT8);
OpenSpB_VECTOR_EXTRACT_TUPLES(int16_t, INT16);
OpenSpB_VECTOR_EXTRACT_TUPLES(int32_t, INT32);
OpenSpB_VECTOR_EXTRACT_TUPLES(int64_t, INT64);
OpenSpB_VECTOR_EXTRACT_TUPLES(uint8_t, UINT8);
OpenSpB_VECTOR_EXTRACT_TUPLES(uint16_t, UINT16);
OpenSpB_VECTOR_EXTRACT_TUPLES(uint32_t, UINT32);
OpenSpB_VECTOR_EXTRACT_TUPLES(uint64_t, UINT64);
OpenSpB_VECTOR_EXTRACT_TUPLES(float, FP32);
OpenSpB_VECTOR_EXTRACT_TUPLES(double, FP64);
#undef OpenSpB_VECTOR_EXTRACT_TUPLES

/**
 * @callgraph
 */
OpenSpB_info OpenSpB_vector_free(OpenSpB_vector *v)
{
    OpenSpB_vector_format_value_ext format =
        (*v)->format_type;
    switch (format)
    {
    case (OpenSpB_dense):
        return OpenSpB_vector_Dense_free(v);
    case (OpenSpB_sparse):
        return OpenSpB_vector_Sparse_free(v);
    default:
        OpenSpB_Error("Unknown Vector Format \
in the OpenSpB_vector_free !\n",
                      __FILE__, __LINE__);
        break;
    }
    return OpenSpB_failure;
}

#define OpenSpB_VECTOR_BUILD(type, T)               \
    OpenSpB_info OpenSpB_vector_build_##T(          \
        OpenSpB_vector v,                           \
        const OpenSpB_index *indices,               \
        const type *values,                         \
        OpenSpB_index n)                            \
    {                                               \
        OpenSpB_vector_format_value_ext format =    \
            v->format_type;                         \
        switch (format)                             \
        {                                           \
        case (OpenSpB_dense):                       \
            return OpenSpB_vector_Dense_build_##T(  \
                (OpenSpB_vector_Dense *)v,          \
                indices, values, n);                \
        case (OpenSpB_sparse):                      \
            return OpenSpB_vector_Sparse_build_##T( \
                (OpenSpB_vector_Sparse *)v,         \
                indices, values, n);                \
        default:                                    \
            OpenSpB_Error("Unknown Vector Format \
in the OpenSpB_vector_build_" #T " !\n",            \
                          __FILE__, __LINE__);      \
            break;                                  \
        }                                           \
        return OpenSpB_failure;                     \
    }
OpenSpB_VECTOR_BUILD(bool, BOOL);
OpenSpB_VECTOR_BUILD(int8_t, INT8);
OpenSpB_VECTOR_BUILD(int16_t, INT16);
OpenSpB_VECTOR_BUILD(int32_t, INT32);
OpenSpB_VECTOR_BUILD(int64_t, INT64);
OpenSpB_VECTOR_BUILD(uint8_t, UINT8);
OpenSpB_VECTOR_BUILD(uint16_t, UINT16);
OpenSpB_VECTOR_BUILD(uint32_t, UINT32);
OpenSpB_VECTOR_BUILD(uint64_t, UINT64);
OpenSpB_VECTOR_BUILD(float, FP32);
OpenSpB_VECTOR_BUILD(double, FP64);
#undef OpenSpB_VECTOR_BUILD

/////////////////////////////////////////Dense Vector////////////////////////////////////
OpenSpB_info OpenSpB_vector_Dense_new(
    OpenSpB_vector *v_in,
    OpenSpB_type type,
    OpenSpB_index n)
{
    OpenSpB_vector_Dense **v =
        (OpenSpB_vector_Dense **)v_in;

    *v = (OpenSpB_vector_Dense *)
        OpenSpB_aligned_malloc(
            sizeof(OpenSpB_vector_Dense));
    (*v)->vector_base.format_type = OpenSpB_dense;
    (*v)->vector_base.element_type = type;
    switch (type)
    {
        OpenSpB_TYPE_CASES(v_in);
    }
    size_t size = (*v_in)->element_size;
    if (!(*v))
        return OpenSpB_failure;
    (*v)->values = OpenSpB_aligned_malloc(n * size);
    if (!(*v)->values)
        return OpenSpB_failure;
    // The entire vector is initialized to zero.
    memset((*v)->values, 0, n * size);
    // Set the vector length.
    (*v)->vector_base.n = n;
    return OpenSpB_success;
}

OpenSpB_info OpenSpB_vector_Dense_dup(
    OpenSpB_vector *v1,
    OpenSpB_vector v2)
{
    OpenSpB_vector_Dense **v = (OpenSpB_vector_Dense **)v1;
    *v = (OpenSpB_vector_Dense *)
        OpenSpB_aligned_malloc(
            sizeof(OpenSpB_vector_Dense));
    (*v)->vector_base.format_type = OpenSpB_dense;
    OpenSpB_type type = v2->element_type;
    (*v)->vector_base.element_type = type;
    switch (type)
    {
        OpenSpB_TYPE_CASES(v1);
    }
    size_t size = (*v1)->element_size;
    OpenSpB_index n = v2->n;
    (*v1)->n = n;
    (*v1)->element_size = size;

    if (!(*v))
        return OpenSpB_failure;

    (*v)->values = OpenSpB_aligned_malloc(n * size);
    if (!(*v)->values)
        return OpenSpB_failure;

    memcpy((*v)->values,
           ((OpenSpB_vector_Dense *)v2)->values, n * size);
    return OpenSpB_success;
}

//------------------------------------------------------------------------------
// OpenSpB_vector_set_element: set an entry in a vector, w (row) = x
//------------------------------------------------------------------------------
// Set a single scalar, w(row) = x, typecasting from the type of x to
// the type of w as needed.
#define OpenSpB_VECTOR_DENSE_SET(type, T)                      \
    OpenSpB_info OpenSpB_TOKEN_PASTING2(                       \
        OpenSpB_vector_Dense_setElement_, T)(                  \
        OpenSpB_vector w,                                      \
        type x,                                                \
        OpenSpB_index row)                                     \
    {                                                          \
        ((type *)(((OpenSpB_vector_Dense *)w)->values))[row] = \
            x;                                                 \
        return OpenSpB_success;                                \
    }

OpenSpB_VECTOR_DENSE_SET(bool, BOOL);
OpenSpB_VECTOR_DENSE_SET(int8_t, INT8);
OpenSpB_VECTOR_DENSE_SET(int16_t, INT16);
OpenSpB_VECTOR_DENSE_SET(int32_t, INT32);
OpenSpB_VECTOR_DENSE_SET(int64_t, INT64);
OpenSpB_VECTOR_DENSE_SET(uint8_t, UINT8);
OpenSpB_VECTOR_DENSE_SET(uint16_t, UINT16);
OpenSpB_VECTOR_DENSE_SET(uint32_t, UINT32);
OpenSpB_VECTOR_DENSE_SET(uint64_t, UINT64);
OpenSpB_VECTOR_DENSE_SET(float, FP32);
OpenSpB_VECTOR_DENSE_SET(double, FP64);

#undef OpenSpB_VECTOR_DENSE_SET

//------------------------------------------------------------------------------
// OpenSpB_vector_extract_element: extract a single entry from a vector
//------------------------------------------------------------------------------
// Extract a single entry, x = v(i), typecasting from the type of v to the type
// of x, as needed.

// Returns OpenSpB_success if v(i) is present, and sets x to its value.

#define OpenSpB_VECTOR_DENSE_EXTRACT(type, T)                   \
    OpenSpB_info OpenSpB_TOKEN_PASTING2(                        \
        OpenSpB_vector_Dense_extractElement_, T)(               \
        type * x,                                               \
        const OpenSpB_vector v,                                 \
        OpenSpB_index i)                                        \
    {                                                           \
        if (v->element_type != OpenSpB_##T)                     \
            OpenSpB_Error("The data types of the vector \
elements and the target do not match \
in OpenSpB_vector_Dense_extractElement_" #T "!\n",              \
                          __FILE__, __LINE__);                  \
        (*x) =                                                  \
            ((type *)(((OpenSpB_vector_Dense *)v)->values))[i]; \
        return OpenSpB_success;                                 \
    }

OpenSpB_VECTOR_DENSE_EXTRACT(bool, BOOL);
OpenSpB_VECTOR_DENSE_EXTRACT(int8_t, INT8);
OpenSpB_VECTOR_DENSE_EXTRACT(int16_t, INT16);
OpenSpB_VECTOR_DENSE_EXTRACT(int32_t, INT32);
OpenSpB_VECTOR_DENSE_EXTRACT(int64_t, INT64);
OpenSpB_VECTOR_DENSE_EXTRACT(uint8_t, UINT8);
OpenSpB_VECTOR_DENSE_EXTRACT(uint16_t, UINT16);
OpenSpB_VECTOR_DENSE_EXTRACT(uint32_t, UINT32);
OpenSpB_VECTOR_DENSE_EXTRACT(uint64_t, UINT64);
OpenSpB_VECTOR_DENSE_EXTRACT(float, FP32);
OpenSpB_VECTOR_DENSE_EXTRACT(double, FP64);

#undef OpenSpB_VECTOR_DENSE_SET

//------------------------------------------------------------------------------
// OpenSpB_vector_extract_tuples: extract all tuples from a vector
//------------------------------------------------------------------------------

// SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2022, All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

//------------------------------------------------------------------------------

// Extracts all tuples from a column, like [I,~,X] = find (v).  If
// any parameter I and/or X is NULL, then that component is not extracted.  The
// size of the I and X arrays (those that are not NULL) is given by nvals,
// which must be at least as large as OpenSpB_nvals (&nvals, v).  The values in the
// typecasted to the type of X, as needed.

// If any parameter I and/or X is NULL, that component is not extracted.  So to
// extract just the row indices, pass I as non-NULL, and X as NULL.  This is
// like [I,~,~] = find (v).

// If v is iso and X is not NULL, the iso scalar vx [0] is expanded into X.

#define OpenSpB_VECTOR_DENSE_EXTRACT_TUPLES(type, T)                    \
    OpenSpB_info OpenSpB_TOKEN_PASTING2(                                \
        OpenSpB_vector_Dense_extractTuples_, T)(                        \
        OpenSpB_index * I,                                              \
        type * X,                                                       \
        OpenSpB_index * p_nvals,                                        \
        const OpenSpB_vector v)                                         \
    {                                                                   \
        if (*p_nvals != v->n)                                           \
        {                                                               \
            return OpenSpB_failure;                                     \
        }                                                               \
        else                                                            \
        {                                                               \
            for (OpenSpB_index i = 0; i < *p_nvals; i++)                \
            {                                                           \
                I[i] = i;                                               \
                X[i] =                                                  \
                    ((type *)(((OpenSpB_vector_Dense *)v)->values))[i]; \
            }                                                           \
            return OpenSpB_success;                                     \
        }                                                               \
    }

OpenSpB_VECTOR_DENSE_EXTRACT_TUPLES(bool, BOOL);
OpenSpB_VECTOR_DENSE_EXTRACT_TUPLES(int8_t, INT8);
OpenSpB_VECTOR_DENSE_EXTRACT_TUPLES(int16_t, INT16);
OpenSpB_VECTOR_DENSE_EXTRACT_TUPLES(int32_t, INT32);
OpenSpB_VECTOR_DENSE_EXTRACT_TUPLES(int64_t, INT64);
OpenSpB_VECTOR_DENSE_EXTRACT_TUPLES(uint8_t, UINT8);
OpenSpB_VECTOR_DENSE_EXTRACT_TUPLES(uint16_t, UINT16);
OpenSpB_VECTOR_DENSE_EXTRACT_TUPLES(uint32_t, UINT32);
OpenSpB_VECTOR_DENSE_EXTRACT_TUPLES(uint64_t, UINT64);
OpenSpB_VECTOR_DENSE_EXTRACT_TUPLES(float, FP32);
OpenSpB_VECTOR_DENSE_EXTRACT_TUPLES(double, FP64);

#undef OpenSpB_VECTOR_DENSE_EXTRACT_TUPLES

OpenSpB_info OpenSpB_vector_Dense_free(OpenSpB_vector *v_in)
{
    OpenSpB_vector_Dense **v =
        (OpenSpB_vector_Dense **)v_in;
    if ((*v)->values)
    {
        free((*v)->values);
        free(*v);
        return OpenSpB_success;
    }
    else
    {
        return OpenSpB_failure;
    }
}

#define OpenSpB_VECTOR_DENSE_BUILD(type, T)      \
    OpenSpB_info OpenSpB_vector_Dense_build_##T( \
        OpenSpB_vector_Dense *w,                 \
        const OpenSpB_index *indices,            \
        const type *values,                      \
        OpenSpB_index n)                         \
    {                                            \
        OpenSpB_index n_w = w->vector_base.n;    \
        OpenSpB_index element_size =             \
            w->vector_base.element_size;         \
        memset(w->values, 0,                     \
               n_w *element_size);               \
                                                 \
        for (OpenSpB_index i = 0; i < n; ++i)    \
        {                                        \
            ((type *)w->values)[indices[i]] =    \
                values[i];                       \
        }                                        \
                                                 \
        return OpenSpB_success;                  \
    }

OpenSpB_VECTOR_DENSE_BUILD(bool, BOOL);
OpenSpB_VECTOR_DENSE_BUILD(int8_t, INT8);
OpenSpB_VECTOR_DENSE_BUILD(int16_t, INT16);
OpenSpB_VECTOR_DENSE_BUILD(int32_t, INT32);
OpenSpB_VECTOR_DENSE_BUILD(int64_t, INT64);
OpenSpB_VECTOR_DENSE_BUILD(uint8_t, UINT8);
OpenSpB_VECTOR_DENSE_BUILD(uint16_t, UINT16);
OpenSpB_VECTOR_DENSE_BUILD(uint32_t, UINT32);
OpenSpB_VECTOR_DENSE_BUILD(uint64_t, UINT64);
OpenSpB_VECTOR_DENSE_BUILD(float, FP32);
OpenSpB_VECTOR_DENSE_BUILD(double, FP64);
#undef OpenSpB_VECTOR_DENSE_BUILD

/////////////////////////////////////////Sparse Vector////////////////////////////////////
OpenSpB_info OpenSpB_vector_Sparse_grow(
    OpenSpB_vector_Sparse *v,
    OpenSpB_index capacity)
{
    if (capacity > v->capacity)
    {
        size_t size =
            v->vector_base.element_size;
        if (v->values)
            v->values =
                OpenSpB_aligned_realloc(
                    v->values, size * capacity);
        else
            v->values =
                OpenSpB_aligned_malloc(
                    size * capacity);
        assert(v->values);
        if (v->indices)
            v->indices = (OpenSpB_index *)
                OpenSpB_aligned_realloc(
                    v->indices,
                    sizeof(OpenSpB_index) * capacity);
        else
            v->indices = (OpenSpB_index *)
                OpenSpB_aligned_malloc(
                    sizeof(OpenSpB_index) * capacity);
        assert(v->indices);
        v->capacity = capacity;
    }
    return OpenSpB_success;
}

OpenSpB_info OpenSpB_vector_Sparse_new(
    OpenSpB_vector *v_in,
    OpenSpB_type type,
    OpenSpB_index n)
{
    OpenSpB_vector_Sparse **v =
        (OpenSpB_vector_Sparse **)v_in;

    *v = (OpenSpB_vector_Sparse *)
        OpenSpB_aligned_malloc(
            sizeof(OpenSpB_vector_Sparse));
    (*v)->vector_base.format_type =
        OpenSpB_sparse;
    (*v)->vector_base.element_type =
        type;
    (*v)->vector_base.n = n;
    (*v)->values = NULL;
    (*v)->indices = NULL;
    (*v)->nnz = 0;
    (*v)->capacity = 0;
    (*v)->is_sorted = true;
    switch (type)
    {
        OpenSpB_TYPE_CASES(v_in);
    }
    return OpenSpB_success;
}

static OpenSpB_index OpenSpB_vector_Sparse_capacity(
    OpenSpB_vector_Sparse *v)
{
    return v->capacity;
}

static OpenSpB_index OpenSpB_vector_Sparse_nnz(
    OpenSpB_vector_Sparse *v)
{
    return v->nnz;
}

static bool OpenSpB_vector_Sparse_findElement(
    OpenSpB_vector_Sparse *v,
    OpenSpB_index target,
    OpenSpB_index *ans)
{
    if (v->nnz)
    {
        OpenSpB_index left = 1;
        OpenSpB_index right = v->nnz;

        while (left <= right)
        {
            OpenSpB_index mid = ((right - left) >> 1) + left;
            OpenSpB_index val = v->indices[mid - 1];
            if (target == val)
            {
                *ans = mid - 1;
                return true;
            }
            else if (target < val)
            {
                *ans = mid - 1;
                right = mid - 1;
            }
            else
            {
                *ans = mid;
                left = mid + 1;
            }
        }
    }
    return false;
}

//------------------------------------------------------------------------------
// OpenSpB_vector_set_element: set an entry in a vector, w (row) = x
//------------------------------------------------------------------------------
// Set a single scalar, w(row) = x, typecasting from the type of x to
// the type of w as needed.
#define OpenSpB_VECTOR_SPARSE_SET(type, T)                           \
    OpenSpB_info OpenSpB_vector_Sparse_setElement_##T /* w(i) = x */ \
        (                                                            \
            OpenSpB_vector w, /* vector to modify           */       \
            type x,           /* scalar to assign to w(i) */         \
            OpenSpB_index i   /* index                  */           \
        )                                                            \
    {                                                                \
        OpenSpB_vector_Sparse *v = (OpenSpB_vector_Sparse *)w;       \
        size_t size = w->element_size;                               \
                                                                     \
        assert(i < w->n);                                            \
        OpenSpB_index index = 0;                                     \
        if (OpenSpB_vector_Sparse_findElement(v, i, &index))         \
        {                                                            \
            if (x)                                                   \
                ((type *)v->values)[index] = x;                      \
            else                                                     \
            {                                                        \
                v->nnz--;                                            \
                memmove(v->values + index * size,                    \
                        v->values + (index + 1) * size,              \
                        size * (v->nnz - index));                    \
                memmove(v->indices + index,                          \
                        v->indices + index + 1,                      \
                        sizeof(OpenSpB_index) * (v->nnz - index));   \
            }                                                        \
        }                                                            \
        else if (x)                                                  \
        {                                                            \
            OpenSpB_index v_capacity =                               \
                OpenSpB_vector_Sparse_capacity(v);                   \
            OpenSpB_index v_size = OpenSpB_vector_Sparse_nnz(v);     \
            if (v_capacity <= v_size)                                \
            {                                                        \
                OpenSpB_vector_Sparse_grow(                          \
                    v,                                               \
                    Vector_compute_next_grow((v_capacity)));         \
            }                                                        \
            if (index < v_size)                                      \
            {                                                        \
                memmove(v->values + (index + 1) * size,              \
                        v->values + index * size,                    \
                        size * (v_size - index));                    \
                memmove(v->indices + index + 1,                      \
                        v->indices + index,                          \
                        sizeof(OpenSpB_index) * (v_size - index));   \
            }                                                        \
            ((type *)v->values)[index] = x;                          \
            v->indices[index] = i;                                   \
            v->nnz++;                                                \
        }                                                            \
        return OpenSpB_success;                                      \
    }
OpenSpB_VECTOR_SPARSE_SET(bool, BOOL);
OpenSpB_VECTOR_SPARSE_SET(int8_t, INT8);
OpenSpB_VECTOR_SPARSE_SET(int16_t, INT16);
OpenSpB_VECTOR_SPARSE_SET(int32_t, INT32);
OpenSpB_VECTOR_SPARSE_SET(int64_t, INT64);
OpenSpB_VECTOR_SPARSE_SET(uint8_t, UINT8);
OpenSpB_VECTOR_SPARSE_SET(uint16_t, UINT16);
OpenSpB_VECTOR_SPARSE_SET(uint32_t, UINT32);
OpenSpB_VECTOR_SPARSE_SET(uint64_t, UINT64);
OpenSpB_VECTOR_SPARSE_SET(float, FP32);
OpenSpB_VECTOR_SPARSE_SET(double, FP64);

#undef OpenSpB_VECTOR_SPARSE_SET

//------------------------------------------------------------------------------
// OpenSpB_vector_extract_element: extract a single entry from a vector
//------------------------------------------------------------------------------
// Extract a single entry, x = v(i), typecasting from the type of v to the type
// of x, as needed.

// Returns OpenSpB_success if v(i) is present, and sets x to its value.

#define OpenSpB_VECTOR_SPARSE_EXTRACT(type, T)             \
    OpenSpB_info OpenSpB_vector_Sparse_extractElement_##T( \
        type *x,                                           \
        const OpenSpB_vector v_in,                         \
        OpenSpB_index i)                                   \
    {                                                      \
        if (v_in->element_type != OpenSpB_##T)             \
            OpenSpB_Error("The data types of the vector elements \
and the target do not match in \
OpenSpB_vector_Sparse_extractElement!\n",                  \
                          __FILE__, __LINE__);             \
                                                           \
        OpenSpB_vector_Sparse *v =                         \
            (OpenSpB_vector_Sparse *)v_in;                 \
                                                           \
        OpenSpB_index index = 0;                           \
        if (OpenSpB_vector_Sparse_findElement(             \
                v, i, &index))                             \
        {                                                  \
            *x = ((type *)v->values)[index];               \
            return OpenSpB_success;                        \
        }                                                  \
        else                                               \
        {                                                  \
            return OpenSpB_no_value;                       \
        }                                                  \
    }
OpenSpB_VECTOR_SPARSE_EXTRACT(bool, BOOL);
OpenSpB_VECTOR_SPARSE_EXTRACT(int8_t, INT8);
OpenSpB_VECTOR_SPARSE_EXTRACT(int16_t, INT16);
OpenSpB_VECTOR_SPARSE_EXTRACT(int32_t, INT32);
OpenSpB_VECTOR_SPARSE_EXTRACT(int64_t, INT64);
OpenSpB_VECTOR_SPARSE_EXTRACT(uint8_t, UINT8);
OpenSpB_VECTOR_SPARSE_EXTRACT(uint16_t, UINT16);
OpenSpB_VECTOR_SPARSE_EXTRACT(uint32_t, UINT32);
OpenSpB_VECTOR_SPARSE_EXTRACT(uint64_t, UINT64);
OpenSpB_VECTOR_SPARSE_EXTRACT(float, FP32);
OpenSpB_VECTOR_SPARSE_EXTRACT(double, FP64);

#undef OpenSpB_VECTOR_SPARSE_SET

//------------------------------------------------------------------------------
// OpenSpB_vector_extract_tuples: extract all tuples from a vector
//------------------------------------------------------------------------------

#define OpenSpB_VECTOR_SPARSE_EXTRACT_TUPLES(type, T)     \
    OpenSpB_info OpenSpB_TOKEN_PASTING2(                  \
        OpenSpB_vector_Sparse_extractTuples_, T)(         \
        OpenSpB_index * I,                                \
        type * X,                                         \
        OpenSpB_index * p_nvals,                          \
        const OpenSpB_vector v)                           \
    {                                                     \
        if (*p_nvals != v->n)                             \
        {                                                 \
            return OpenSpB_failure;                       \
        }                                                 \
        else                                              \
        {                                                 \
            for (OpenSpB_index i = 0; i < *p_nvals; i++)  \
            {                                             \
                I[i] = i;                                 \
                type value = (type)0;                     \
                OpenSpB_vector_Sparse_extractElement_##T( \
                    &value, v, i);                        \
                X[i] = value;                             \
            }                                             \
            return OpenSpB_success;                       \
        }                                                 \
    }

OpenSpB_VECTOR_SPARSE_EXTRACT_TUPLES(bool, BOOL);
OpenSpB_VECTOR_SPARSE_EXTRACT_TUPLES(int8_t, INT8);
OpenSpB_VECTOR_SPARSE_EXTRACT_TUPLES(int16_t, INT16);
OpenSpB_VECTOR_SPARSE_EXTRACT_TUPLES(int32_t, INT32);
OpenSpB_VECTOR_SPARSE_EXTRACT_TUPLES(int64_t, INT64);
OpenSpB_VECTOR_SPARSE_EXTRACT_TUPLES(uint8_t, UINT8);
OpenSpB_VECTOR_SPARSE_EXTRACT_TUPLES(uint16_t, UINT16);
OpenSpB_VECTOR_SPARSE_EXTRACT_TUPLES(uint32_t, UINT32);
OpenSpB_VECTOR_SPARSE_EXTRACT_TUPLES(uint64_t, UINT64);
OpenSpB_VECTOR_SPARSE_EXTRACT_TUPLES(float, FP32);
OpenSpB_VECTOR_SPARSE_EXTRACT_TUPLES(double, FP64);

#undef OpenSpB_VECTOR_SPARSE_EXTRACT_TUPLES

OpenSpB_info OpenSpB_vector_Sparse_free(OpenSpB_vector *v_in)
{
    OpenSpB_vector_Sparse **v = (OpenSpB_vector_Sparse **)v_in;
    if ((*v)->values)
    {
        free((*v)->values);
    }
    else
    {
        return OpenSpB_failure;
    }
    if ((*v)->indices)
    {
        free((*v)->indices);
    }
    else
    {
        return OpenSpB_failure;
    }
    if ((*v))
    {
        free(*v);
    }
    else
    {
        return OpenSpB_failure;
    }

    return OpenSpB_success;
}

#define OpenSpB_VECTOR_SPARSE_BUILD(type, T)           \
    OpenSpB_info OpenSpB_vector_Sparse_build_##T(      \
        OpenSpB_vector_Sparse *v,                      \
        const OpenSpB_index *indices,                  \
        const type *values,                            \
        OpenSpB_index n)                               \
    {                                                  \
        OpenSpB_index v_capacity = v->capacity;        \
        OpenSpB_info info;                             \
        if (v_capacity <= n)                           \
        {                                              \
            SAFE_CALL(                                 \
                OpenSpB_vector_Sparse_grow(v, n + 1)); \
            v->capacity = n + 1;                       \
        }                                              \
        memcpy(v->values,                              \
               values, n * sizeof(type));              \
        memcpy(v->indices,                             \
               indices, n * sizeof(OpenSpB_index));    \
        v->nnz = n;                                    \
        return OpenSpB_success;                        \
    }
OpenSpB_VECTOR_SPARSE_BUILD(bool, BOOL);
OpenSpB_VECTOR_SPARSE_BUILD(int8_t, INT8);
OpenSpB_VECTOR_SPARSE_BUILD(int16_t, INT16);
OpenSpB_VECTOR_SPARSE_BUILD(int32_t, INT32);
OpenSpB_VECTOR_SPARSE_BUILD(int64_t, INT64);
OpenSpB_VECTOR_SPARSE_BUILD(uint8_t, UINT8);
OpenSpB_VECTOR_SPARSE_BUILD(uint16_t, UINT16);
OpenSpB_VECTOR_SPARSE_BUILD(uint32_t, UINT32);
OpenSpB_VECTOR_SPARSE_BUILD(uint64_t, UINT64);
OpenSpB_VECTOR_SPARSE_BUILD(float, FP32);
OpenSpB_VECTOR_SPARSE_BUILD(double, FP64);
#undef OpenSpB_VECTOR_SPARSE_BUILD